package dm.ast.model.gen;


import dm.ast.*;
import dm.ast.model.gen.factory.BaseModelFactory;
import dm.ast.model.gen.factory.ComplexityModel;
import dm.ast.model.gen.factory.SelectModelFactory;
import dm.sql.comp.ASTComplex;
import dm.sql.comp.SQLComplexity;
import dm.sql.comp.SQLComplexity.*;
import test.envset.GlobalState;
import test.utility.Randomly;

import java.util.List;

/**
 * 根据模板AST生成SELECT
 */
public class SelectGen extends ASTGenBase {
    static int MAX_USED_TIMES = 13;

    public static int SUBSTMT_CNT = DmSelect.SUBCLAUSE_CNT;
    public ASTGenBase[] gens; //各子句生成器

    //模板相关生成
    private BaseModelFactory baseModelF = null;
    private List<ComplexityModel> selModels = null;
    private int models_cnt = 0;  //模板数量
    private int models_use_times = 0;

    public SelectGen() {
        gens = new ASTGenBase[SUBSTMT_CNT];
        baseModelF = new BaseModelFactory();
        baseModelF.setTestFeatures();  //设置需要测试的功能点

        gens[DmSelect.INDEX_HINT] = new HintsGen();
        gens[DmSelect.INDEX_ELEMENT] = new SelectElementGen();
        gens[DmSelect.INDEX_FROM] = new FromGen();
        gens[DmSelect.INDEX_WHERE] = new WhereGen();
        gens[DmSelect.INDEX_GROUP] = new GroupGen();
        gens[DmSelect.INDEX_HAVING] = new HavingGen();
        gens[DmSelect.INDEX_WITH] = new WithGen();
        gens[DmSelect.INDEX_ORDER] = new OrderGen();
        gens[DmSelect.INDEX_LIMIT] = new LimitGen();
        gens[DmSelect.INDEX_ROWLIMIT] = new RowLimitGen();
        gens[DmSelect.INDEX_TOP] = new TopGen();

        models_use_times = 0;
    }

    public void setBaseModelF(BaseModelFactory baseModelF) {
        this.baseModelF = baseModelF;
    }

    //初始化模板
    public void initModels(int wid_t, int dep_t, boolean withStatement) {
        selModels = SelectModelFactory.selectModels(wid_t, dep_t, withStatement, baseModelF);
        models_cnt = selModels.size();
    }

    public void initModels(ASTComplex comp, boolean withStatement) {
        selModels = SelectModelFactory.selectModels(comp, withStatement, baseModelF);
        models_cnt = selModels.size();
    }

    public void initModels(SQLComplexity comp, boolean withStatement) {
        selModels = SelectModelFactory.selectModels(comp, withStatement, baseModelF);
        models_cnt = selModels.size();
    }

    public void initModels(ComplexLevel comp, boolean withStatement) {
        selModels = SelectModelFactory.selectModels(comp, withStatement, baseModelF);  //通过不同的分配方式，得到了各个元素不同宽度和深度的ComplexityModel模型
        models_cnt = selModels.size();  //models_cnt模型的数量
    }

    //生成
    @Override
    public DmASTNode gen(int astLevel, int wid_t, int dep_t) {
        if (models_cnt == 0) {
            initModels(wid_t, dep_t, withFlag(astLevel));
        }
        //BaseModelFactory.getRanComplexitvModel(selModels) //根据模板权重比例加权,随机返回一个模板
        return genByModel(astLevel, BaseModelFactory.getRanComplexityModel(selModels));
    }

    //生成指定数目的查询列
    public DmASTNode gen(int astLevel, int wid_t, int dep_t, int fields) {
        if (models_cnt == 0) {
            initModels(wid_t, dep_t, withFlag(astLevel));
        }
        return genByModel(astLevel, BaseModelFactory.getRanComplexityModel(selModels), fields);
    }

    //指定类型
    public DmASTNode gen(int astLevel, int wid_t, int dep_t, int fields,List<DmDataType> selTypes) {
        if (models_cnt == 0) {
            initModels(wid_t, dep_t, withFlag(astLevel));
        }
        return genByModel(astLevel, BaseModelFactory.getRanComplexityModel(selModels), fields,selTypes);
    }

    public DmASTNode gen(int astLevel, ASTComplex comp) {
        if (models_cnt == 0) {
            initModels(comp, withFlag(astLevel));
        }
        return genByModel(astLevel, BaseModelFactory.getRanComplexityModel(selModels));
    }

    public DmASTNode gen(int astLevel, SQLComplexity comp) {
        if (models_cnt == 0) {
            initModels(comp, withFlag(astLevel));
        }
        return genByModel(astLevel, BaseModelFactory.getRanComplexityModel(selModels));
    }

    //comp = GlobalState.getGenComplex();  selGen.gen(0 , comp)
    public DmASTNode gen(int astLevel, ComplexLevel comp) {  //通过树的层级和复杂度创建sql？
        if (models_cnt == 0) {
            initModels(comp, withFlag(astLevel));
        }
        //BaseModelFactory.getRanComplexitvModel(selModels) //根据模板权重比例加权,随机返回一个模板
        return genByModel(astLevel, BaseModelFactory.getRanComplexityModel(selModels));
    }

    //根据查询层次判定是否允许with子句
    //仅最外层查询允许使用with
    boolean withFlag(int astLevel) {
        return astLevel == 0;
    }

    //根据模板生成SELECT语句基本框架
    DmASTNode genByModel(int astLevel, ComplexityModel model) {

        return genByModelLow(astLevel, model, 0,null);
    }

    //根据模板生成SELECT语句基本框架
    //指定查询列数
    DmASTNode genByModel(int astLevel, ComplexityModel model, int fields) {
        return genByModelLow(astLevel, model, fields,null);
    }

    DmASTNode genByModel(int astLevel, ComplexityModel model, int fields,List<DmDataType> selTypes) {
        return genByModelLow(astLevel, model, fields,selTypes);
    }

    //模型使用计数
    void modelUseCount() {
        models_use_times++;
        if (models_use_times >= getMaxUsedTimes()) {
            models_use_times = 0;
            if (selModels != null) {
                selModels.clear();
                selModels = null;
                models_cnt = 0;
                baseModelF.setTestFeatures();
            }
        }
    }

    public static int getMaxUsedTimes() {
        return MAX_USED_TIMES;
    }

    public int getUseTimes() {
        return models_use_times;
    }

    public void setSelModels(List<ComplexityModel> selModels) {
        this.selModels = selModels;
    }

    public void setModels_use_times(int models_use_times) {
        this.models_use_times = models_use_times;
    }

    //fields表示查询列的数目，为0表示不限制
    private DmASTNode genByModelLow(int astLevel, ComplexityModel model, int fields,List<DmDataType> selTypes) {
        DmSelect select = new DmSelect();
        select.setTargetWidt(model.comps[DmSelect.INDEX_HINT].wid_t);
        select.setTargetDept(model.comps[DmSelect.INDEX_HINT].dep_t);
        select.setASTLevel(astLevel);

        //处理hints
        if (baseModelF.testFlags[BaseModelFactory.FLAG_HINT]) {
            select.setHints(genHint(astLevel));
        }
        //处理SelectElements，需要生成指定数目的查询列
        if(GlobalState.getOptions().testUnions()){
            select.setSelectElements(
                    genSelectElements(astLevel,model.comps[DmSelect.INDEX_ELEMENT], fields,selTypes));
        }else{
            select.setSelectElements(genSelectElements(astLevel, model.comps[DmSelect.INDEX_ELEMENT], fields));
        }

        //处理From
        DmFrom from = new DmFrom();
        from = genFrom(astLevel,model.comps[DmSelect.INDEX_FROM]);
        select.setFromList(from);

        //处理singlejoin(+)旧式外连接
        DmWhere where = new DmWhere();
        where = genWhere(astLevel, model.comps[DmSelect.INDEX_WHERE]);
        if (from != null && from.fromTables.size() > 1 && baseModelF.testFlags[BaseModelFactory.FLAG_OLDOUTERJOIN]) {
            if(where != null){
                genSingleJoin(astLevel,where);
            }
        }
        //处理where
        select.setWhereClause(where);

        //处理groupby
        if (baseModelF.testFlags[BaseModelFactory.FLAG_GROUP]) {
            select.setGroupbyClause(genGroupby(astLevel, model.comps[DmSelect.INDEX_GROUP]));
            //如果有groupby子句，那么大就率生成的查询列仅由表的列构成
            if (select.getGroupbyClause() != null) {
                if (!Randomly.getBooleanWithSmallProbability()) {
                    select.setSelectElements(genSelectElementsColumns(astLevel, model.comps[DmSelect.INDEX_ELEMENT], fields));
                }
            }
        }
        //处理Having
        if (baseModelF.testFlags[BaseModelFactory.FLAG_HAVING]) {
            select.setHavingClause(genHaving(astLevel, model.comps[DmSelect.INDEX_HAVING]));
            //如果此时groupby子句为空，那么增加一个group by子句
            if (select.subClause[DmSelect.INDEX_GROUP] == null) {
                select.setGroupbyClause(genGroupby(astLevel, new ASTComplex(1, 1)));
            }
        }
        //处理with
        if (baseModelF.testFlags[BaseModelFactory.FLAG_WITH]) {
            select.setWithClause(genDmWith(astLevel, model.comps[DmSelect.INDEX_WITH]));
        }
        //处理top子句
        if (model.comps[DmSelect.INDEX_LIMIT].wid_t == 0 && baseModelF.testFlags[BaseModelFactory.FLAG_TOP]) {
            select.setTopClause(genTop(astLevel, model.comps[DmSelect.INDEX_TOP]));
        }
        //处理limit子句
        if (select.getTopClause() == null) {
            if (baseModelF.testFlags[BaseModelFactory.FLAG_LIMIT]) {
                select.setLimitClause(genLimit(astLevel, model.comps[DmSelect.INDEX_LIMIT]));
            }
        }
        //处理RowLimit子句
        if (select.getTopClause() == null && select.getLimitClause() == null) {
            if (baseModelF.testFlags[BaseModelFactory.FLAG_LIMIT]) {
                if (model.comps[DmSelect.INDEX_ROWLIMIT].wid_t != 0)
                    select.setRowLimitClause(genRowLimit(astLevel, model.comps[DmSelect.INDEX_ROWLIMIT]));
            }
        }
        //处理order
        if (baseModelF.testFlags[BaseModelFactory.FLAG_ORDER]) {
            select.setOrderbyClause(genOrder(astLevel, model.comps[DmSelect.INDEX_ORDER]));
        }
        //设置distinct
        if (Randomly.getBooleanWithSmallProbability()) {
            select.setDistrictFlag(true);
        }
        modelUseCount();
        return select;
    }

    private DmSelectElements genSelectElements(int astLevel, ASTComplex comp, int fields) {
        if (comp.wid_t == 0) {
            throw new IllegalArgumentException();
        }

        DmSelectElements selectElements;
        gens[DmSelect.INDEX_ELEMENT].setTestFlags(baseModelF.testFlags);
        if (fields == 0) {
            selectElements = (DmSelectElements) gens[DmSelect.INDEX_ELEMENT].gen(astLevel, comp.wid_t, comp.dep_t);
        } else {
            selectElements = (DmSelectElements) SelectElementGen.gen(astLevel, comp.wid_t, comp.dep_t, fields, baseModelF.testFlags);
        }
        return selectElements;
    }

    //使用列类型
    private DmSelectElements genSelectElements(int astLevel, ASTComplex comp, int fields,List<DmDataType> selTypes) {
        if (comp.wid_t == 0) {
            throw new IllegalArgumentException();
        }

        DmSelectElements selectElements;
        gens[DmSelect.INDEX_ELEMENT].setTestFlags(baseModelF.testFlags);
        if (selTypes == null) {
            selectElements = genSelectElements(astLevel,comp,fields);
        } else {
            selectElements = (DmSelectElements) SelectElementGen.gen(astLevel, comp.wid_t, comp.dep_t, fields, baseModelF.testFlags,selTypes);
        }
        return selectElements;
    }

    //仅生成由列构成的查询列
    //前用于带groupby的查询语句
    private DmSelectElements genSelectElementsColumns(int astLevel, ASTComplex comp, int fields) {
        if (comp.wid_t == 0) {
            throw new IllegalArgumentException();
        }
        DmSelectElements selectElements;
        gens[DmSelect.INDEX_ELEMENT].setTestFlags(baseModelF.testFlags);
        selectElements = SelectElementGen.genColumns(astLevel, comp.wid_t, comp.dep_t, fields);
        return selectElements;
    }

    private DmHint genHint(int astLevel) {
        return (DmHint) gens[DmSelect.INDEX_HINT].gen(astLevel, 0, 0);
    }

    private DmFrom genFrom(int astLevel, ASTComplex comp) {
        if (comp.wid_t == 0) {
            return null;
        }
        DmFrom dmFrom = (DmFrom) gens[DmSelect.INDEX_FROM].gen(astLevel, comp.wid_t, comp.dep_t);
        if(dmFrom != null){
            gens[DmSelect.INDEX_FROM].setTestFlags(baseModelF.testFlags);
            FromGen.joingen(dmFrom,astLevel,baseModelF);
        }
        return dmFrom;
    }

    private DmWhere genWhere(int astLevel, ASTComplex comp) {
        if (comp.wid_t == 0) {
            return null;
        }
        gens[DmSelect.INDEX_WHERE].setTestFlags(BaseModelFactory.getRandExpTypes());
        DmWhere dmWhere = (DmWhere) gens[DmSelect.INDEX_WHERE].gen(astLevel, comp.wid_t, comp.dep_t);
        return dmWhere;
    }

    private void genSingleJoin(int astLevel,DmWhere where){
        WhereGen.singlejoin(astLevel,where);
        return;
    }

    private DmGroupby genGroupby(int astLevel, ASTComplex comp) {
        if (comp.wid_t == 0) {
            return null;
        }
        //调用groupbyGen里面的gen方法，通过模型中comp的宽度和深度，返回一个DmASTNode对象，所有的Dm元素类都继承了DmASTNode
        //返回的DmASTNode里面包含了groupyby的目标宽度和深度，以及模型的数量？
        DmGroupby dmGroupby = (DmGroupby) gens[DmSelect.INDEX_GROUP].gen(astLevel, comp.wid_t, comp.dep_t);
        if (dmGroupby != null) {
            GroupGen.flag(dmGroupby, baseModelF);
            GroupGen.GroupingSets(dmGroupby);
        }
        return dmGroupby;
    }

    private DmHaving genHaving(int astLevel, ASTComplex comp) {
        if (comp.wid_t == 0) {
            return null;
        }
        gens[DmSelect.INDEX_HAVING].setTestFlags(BaseModelFactory.getRandExpTypes());
        DmHaving dmHaving = (DmHaving) gens[DmSelect.INDEX_HAVING].gen(astLevel, comp.wid_t, comp.dep_t);
        return dmHaving;
    }

    private DmWith genDmWith(int astLevel, ASTComplex comp) {
        if (comp.wid_t == 0) {
            return null;
        }
        //此处astLevel+1，是为了防止with子句中的查询也出现with
        DmWith dmWith = (DmWith) gens[DmSelect.INDEX_WITH].gen(astLevel + 1, comp.wid_t, comp.dep_t);
        return dmWith;
    }

    private DmOrderby genOrder(int astLevel, ASTComplex comp) {
        if (comp.wid_t == 0) {
            return null;
        }
        DmOrderby dmOrder = (DmOrderby) gens[DmSelect.INDEX_ORDER].gen(astLevel, comp.wid_t, comp.dep_t);
        return dmOrder;
    }

    private DmLimit genLimit(int astLevel, ASTComplex comp) {
        if (comp.wid_t == 0) {
            return null;
        }
        DmLimit dmLimit = (DmLimit) gens[DmSelect.INDEX_LIMIT].gen(astLevel, comp.wid_t, comp.dep_t);
        return dmLimit;
    }

    private DmTop genTop(int astLevel, ASTComplex comp) {
        if (comp.wid_t == 0) {
            return null;
        }
        DmTop dmTop = (DmTop) gens[DmSelect.INDEX_TOP].gen(astLevel, comp.wid_t, comp.dep_t);
        return dmTop;
    }

    private DmRowLimit genRowLimit(int astLevel,ASTComplex comp){
        if(comp.wid_t ==  0){
            return null;
        }
        DmRowLimit dmRowLimit = (DmRowLimit) gens[DmSelect.INDEX_ROWLIMIT].gen(astLevel,comp.wid_t,comp.dep_t);
        return dmRowLimit;
    }
}